package gov.dhs.cis.soa.readers.wbm;

import gov.dhs.cis.soa.Utils;
import gov.dhs.cis.soa.data.Processes;
import gov.dhs.cis.soa.data.RelatedProperties;
import gov.dhs.cis.soa.data.Service;
import gov.dhs.cis.soa.readers.ExcelReader;

import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class ReadWBMXMLFile extends ExcelReader {
	XPathFactory xpathFactory = XPathFactory.newInstance();
	String XMI_NS = "http://schema.omg.org/spec/XMI/2.1";
	String WBIM_NS = "http://www.ibm.com/wbim/bomSchema1.0";

    BufferedWriter out = null;
    HSSFWorkbook book = null;
    HSSFSheet sheet = null;
	String[] uc_header = new String[] { "UC#", "Process Name", "Task Name", "Type", "Description", "Inputs", 
			"Outputs", "Service"};  
	String[] ruleset_header = new String[] { "RuleSetID", "RuleSet Name", "Description", "expected Outcome", 
							"RuleSet Domain" };
	String[] rule_sheet_header = new String[] { "Rule ID", "Rule Name", "Priority", "Description"};
    int nCurrentRow = 0;

	private Document getXMLDocument(String inXML) throws Exception {
		InputStream inXMLStream = this.getClass().getClassLoader().getResourceAsStream(inXML);
		DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
		domFactory.setNamespaceAware(true);
		DocumentBuilder builder = domFactory.newDocumentBuilder();
		Document doc = builder.parse(inXMLStream);
		return doc;
	}
	
	private NamespaceContext getNameSpaceCtx () {
		NamespaceContext ctx = new NamespaceContext() {
	        public String getNamespaceURI(String prefix) {
	        	//System.out.println("Name space requested : " + prefix);
	            String uri = null;
	            if (prefix.equals("xmi")) {
	                uri = XMI_NS;
	            } else if (prefix.equals("wbim")) {
		                uri = WBIM_NS;
	            }
	            return uri;
	        }
	       
	        // Dummy implementation - not used!
	        public Iterator getPrefixes(String val) { return null; }
	       
	        // Dummy implemenation - not used!
	        public String getPrefix(String uri) { return null; }
	    };
	    return ctx;
	}
	
	private XPathExpression getXPathExpression(String xpathStr) throws Exception {
		XPath xpath = xpathFactory.newXPath();
		xpath.setNamespaceContext(getNameSpaceCtx());
		// XPath Query for showing all nodes value
		XPathExpression expr = xpath.compile(xpathStr);
		return expr;
	}

	private NodeList evaluate(Object evalOn, XPathExpression expr) throws Exception {
		Object result = expr.evaluate(evalOn, XPathConstants.NODESET);
		NodeList nodes = (NodeList) result;
		return nodes;
	}

	private void printHeader(HSSFSheet toSheet, String[] header, int rowNum) {
		HSSFRow row = toSheet.createRow(rowNum);
		String data = "";
		for(int i = 0; i < header.length; i++) {
			data = (String)header[i];
			HSSFCell cell = row.createCell(i);
			HSSFCellStyle style = book.createCellStyle();
			style.setFillForegroundColor(IndexedColors.LIGHT_ORANGE.getIndex());
			style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
			style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
			style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
			cell.setCellStyle(style);
			cell.setCellValue(data);
		}
	}

	private void addCellData(int cellLoc, HSSFRow row, String data) {
		HSSFCell cell = row.createCell(cellLoc);
		cell.setCellValue(data);
	}
	
	public HashMap<String, String> loadUCs() {
		HSSFWorkbook w = openFile("UCReport - goodrun 02072011.xls");
		HSSFSheet s1 = w.getSheet("Main UCs");
		int rows = s1.getLastRowNum();
		HashMap<String, String> all_UCs = new HashMap<String, String>();
		
		for (int n = 1; n < rows; n++) {
			HSSFRow row = s1.getRow(n);
			if(row == null)
				continue;
			String UC_Num = getCellStringValue(w, row, "A");	//UC#
			if(Utils.isEmpty(UC_Num)) {
				continue;
			}
			String UC_Name = getCellStringValue(w, row, "B"); //UC name
			if(Utils.isEmpty(UC_Name)) {
				continue;
			}
			String UC_Type = getCellStringValue(w, row, "H"); //UC Type
			if(Utils.isEmpty(UC_Type)) {
				continue;
			}
			all_UCs.put(UC_Type + " - " + UC_Name, UC_Num);
		}
		return all_UCs;
	}
	
	private ArrayList<Service> extractWBMProcesses(Document doc) throws Exception {
		ArrayList<Service> servList = new ArrayList<Service>();

		NodeList nodes = evaluate(doc, getXPathExpression("//wbim:processModel/wbim:processes/wbim:process"));
		XPathExpression descExpr = getXPathExpression("wbim:description/text()");
		//XPathExpression tasksExpr = getXPathExpression("wbim:flowContent/(wbim:task || wbim:callToBusinessRulesTask)");
		String taskFileter = "wbim:flowContent/*[name()='wbim:task' or name()='wbim:humanTask' " +
				"or name()='wbim:callToBusinessRulesTask' or name()='wbim:businessRulesTask']";
		XPathExpression tasksExpr = getXPathExpression(taskFileter);
		XPathExpression inputExpr = getXPathExpression("wbim:inputs/wbim:input");
		XPathExpression outputExpr = getXPathExpression("wbim:outputs/wbim:output");
		XPathExpression servNameExpr = getXPathExpression("wbim:classifiers/wbim:classifierValue");
		
		HashMap<String, String> all_UCs = loadUCs();
		
		String procName = "";
		String desc = "";
		int cellNum = 0;
		HSSFRow row = null;
		HSSFCell cell = null;
		for(int i = 0; i < nodes.getLength(); i++) {
			Node servNode = nodes.item(i);
			
				cellNum = 1; //to skip the UC# column, will be filled in manually looking at reqpro report
				row = sheet.createRow(nCurrentRow++);

			procName = servNode.getAttributes().getNamedItem("name").getNodeValue();
			int idx = procName.indexOf("##");
			if(idx > 0) {
				procName = procName.substring(idx+2);
			}
			//lookup UC numbers..
			String ucNumber = all_UCs.get(procName);
			if(Utils.isEmpty(ucNumber)) {
				Set<String> keys = all_UCs.keySet();
				String searchKey = procName.substring(2, procName.length()/2);
				for(String key : keys) {
					if (key.indexOf(searchKey) > -1) {
						ucNumber = all_UCs.get(key);
					}
				}
			}
			desc = ""; //getDescription(descExpr, servNode);

			String inputs = ""; //getParameters(inputExpr, servNode);
			String outputs = ""; //getParameters(outputExpr, servNode);

			addCellData(0, row, ucNumber);
			addCellData(cellNum++, row, procName);
			addCellData(cellNum++, row, "None");
			addCellData(cellNum++, row, "ROOT");
			addCellData(cellNum++, row, desc);
			addCellData(cellNum++, row, inputs);
			addCellData(cellNum++, row, outputs);

			NodeList taskNodes = evaluate(servNode, tasksExpr);
			String taskname = null;
			String taskDesc = null;
			String servName = "";
			String type = "";
			for(int j = 0; j < taskNodes.getLength(); j++) {
				cellNum = 1;  //to skip the UC# column, will be filled in manually looking at reqpro report
				taskname = null;
				taskDesc = null;
				servName = "";
				type = "";
				row = sheet.createRow(nCurrentRow++);

				Node taskNode = taskNodes.item(j);
				taskname = taskNode.getAttributes().getNamedItem("name").getNodeValue();
				if("wbim:task".equalsIgnoreCase(taskNode.getNodeName())) {
					type = "TASK";
					servName = getRelatedServices(servNameExpr, taskNode);
				} else if("wbim:humanTask".equalsIgnoreCase(taskNode.getNodeName())) {
					type = "HUMAN";
					servName = "";
				} else {
					if("wbim:businessRulesTask".equalsIgnoreCase(taskNode.getNodeName()))
						type = "RULE(L)";
					else
						type = "RULE(G)";						
					try {
						servName = taskNode.getAttributes().getNamedItem("businessRulesTask").getNodeValue();
						taskNode = findBusinessRuleTask(doc, servName);
						idx = servName.indexOf("##");
						if(idx > 0) {
							servName = servName.substring(idx+2);
						}
					} catch(Exception e) {
					}
				}
				taskDesc = getDescription(descExpr, taskNode);
				inputs = getParameters(inputExpr, taskNode);
				outputs = getParameters(outputExpr, taskNode);

				addCellData(0, row, ucNumber);
				addCellData(cellNum++, row, procName);
				addCellData(cellNum++, row, taskname);
				addCellData(cellNum++, row, type);
				addCellData(cellNum++, row, taskDesc);
				addCellData(cellNum++, row, inputs);
				addCellData(cellNum++, row, outputs);
				addCellData(cellNum++, row, servName);
			}
		}
		return servList;
	}
	
	private void extractWBMProcRules(Document doc, String defWIPT) throws Exception {
		NodeList nodes = evaluate(doc, getXPathExpression("//wbim:processModel/wbim:businessRulesTasks/wbim:businessRulesTask"));
		XPathExpression descExpr = getXPathExpression("wbim:description/text()");

		String rulesFilter = "wbim:businessRules/wbim:businessRule";
		XPathExpression rulesExpr = getXPathExpression(rulesFilter);
		XPathExpression inputExpr = getXPathExpression("wbim:inputs/wbim:input");
		XPathExpression outputExpr = getXPathExpression("wbim:outputs/wbim:output");
		XPathExpression wiptNameExpr = getXPathExpression("wbim:classifiers/wbim:classifierValue");
		
		String ruleSetName = "";
		String desc = "";
		int cellNum = 0;
		HSSFRow row = null;

		for(int i = 0; i < nodes.getLength(); i++) {
			Node ruleSetNode = nodes.item(i);
			
			cellNum = 0;
			row = sheet.createRow(nCurrentRow++);

			ruleSetName = ruleSetNode.getAttributes().getNamedItem("name").getNodeValue();
			int idx = ruleSetName.indexOf("##");
			if(idx > 0) {
				ruleSetName = ruleSetName.substring(idx+2);
			}
			desc = getDescription(descExpr, ruleSetNode);

//			String inputs = getParameters(inputExpr, ruleSetNode);
//			String outputs = getParameters(outputExpr, ruleSetNode);
			String wipt = getRelatedWIPT(wiptNameExpr, ruleSetNode);
			if(Utils.isEmpty(wipt))
				wipt = defWIPT;
			
			addCellData(cellNum++, row, ""+nCurrentRow);
			addCellData(cellNum++, row, ruleSetName);
			addCellData(cellNum++, row, desc);
			addCellData(cellNum++, row, "");
			addCellData(cellNum++, row, wipt);
//			addCellData(cellNum++, row, inputs);
//			addCellData(cellNum++, row, outputs);

			NodeList taskNodes = evaluate(ruleSetNode, rulesExpr);
			addRulesetRulesSheet(ruleSetName, desc, nCurrentRow, taskNodes, descExpr);
		}
	}

	private void addRulesetRulesSheet(String ruleSetName, String ruleSetDesc, int rulesetRowNum, 
			NodeList taskNodes, XPathExpression descExpr) throws Exception {
		int cellNum = 0;
		HSSFRow row = null;
		
		int nRuleSheetRow = 0;
	    HSSFSheet rule_sheet = book.createSheet(rulesetRowNum + "-" + ruleSetName);
		row = rule_sheet.createRow(nRuleSheetRow++);
		
		cellNum = 0;
	    addCellData(cellNum++, row, "RuleSetID");
	    addCellData(cellNum++, row, ""+rulesetRowNum);
		row = rule_sheet.createRow(nRuleSheetRow++);
		cellNum = 0;
	    addCellData(cellNum++, row, "RuleSet Name");
	    addCellData(cellNum++, row, ruleSetName);
		row = rule_sheet.createRow(nRuleSheetRow++);
		cellNum = 0;
	    addCellData(cellNum++, row, "Description");
	    addCellData(cellNum++, row, ruleSetDesc);
	    
	    printHeader(rule_sheet, rule_sheet_header, nRuleSheetRow++);

		if(taskNodes == null)
			return;
		
	    String ruleName = null;
		String desc = null;
		cellNum = 0;
		for(int j = 0; j < taskNodes.getLength(); j++) {
			cellNum = 0;
			ruleName = null;
			desc = null;
			row = rule_sheet.createRow(nRuleSheetRow++);

			Node taskNode = taskNodes.item(j);
			ruleName = taskNode.getAttributes().getNamedItem("name").getNodeValue();
			desc = getDescription(descExpr, taskNode);

			addCellData(cellNum++, row, ""+(j+1));
			addCellData(cellNum++, row, ruleName);
			addCellData(cellNum++, row, "5");
			addCellData(cellNum++, row, desc);
		}
	}

	private String getDescription(XPathExpression descExpr, Node nodeElem)throws Exception {
		String desc = "";
		NodeList descNodes = evaluate(nodeElem, descExpr);
		if(descNodes != null && descNodes.getLength() > 0) {
			desc = descNodes.item(0).getNodeValue();
		}
		return desc;
	}

	private String getParameters(XPathExpression inputExpr, Node nodeElem) throws Exception {
		String ret = "";
		NodeList nodes = evaluate(nodeElem, inputExpr);
		if(nodes == null || nodes.getLength() == 0) 
			return ret;
		String data = "";
		for(int i = 0; i < nodes.getLength(); i++) {
			Node node = nodes.item(i);
			if(i > 0)
				ret += ",";
			try {
				ret += node.getAttributes().getNamedItem("associatedData").getNodeValue();
			} catch (Exception e) {
			}
		}
		ret = ret.replaceAll("##", ".");
		return ret;
	}

	private String getRelatedServices(XPathExpression servExpr, Node nodeElem) throws Exception {
		String ret = "";
		NodeList nodes = evaluate(nodeElem, servExpr);
		if(nodes == null || nodes.getLength() == 0) 
			return ret;
		String data = "";
		String toToken = "Srv##";
		for(int i = 0; i < nodes.getLength(); i++) {
			Node node = nodes.item(i);
			data = node.getAttributes().getNamedItem("name").getNodeValue();
			int idx = data.indexOf(toToken);
			if(idx > 0) {
				data = data.substring(idx+toToken.length());
				if (ret.length() > 0)
					ret += ",";
				ret += data;
			}
		}
		return ret;
	}

	private String getRelatedWIPT(XPathExpression servExpr, Node nodeElem) throws Exception {
		String ret = "";
		NodeList nodes = evaluate(nodeElem, servExpr);
		if(nodes == null || nodes.getLength() == 0) 
			return ret;
		String data = "";
		String toToken = "WIPT##";
		for(int i = 0; i < nodes.getLength(); i++) {
			Node node = nodes.item(i);
			data = node.getAttributes().getNamedItem("name").getNodeValue();
			int idx = data.indexOf(toToken);
			if(idx >= 0) {
				data = data.substring(idx+toToken.length());
				if (ret.length() > 0)
					ret += ",";
				ret += data;
			}
		}
		return ret;
	}

	private Node findBusinessRuleTask(Document doc, String ruleName)throws Exception {
		Node nodeElem = null;
		String ruleExpr = "//wbim:processModel/wbim:businessRulesTasks/wbim:businessRulesTask[@name='" + ruleName + "']";
		NodeList nodes = evaluate(doc, getXPathExpression(ruleExpr));
		if(nodes != null && nodes.getLength() > 0) {
			nodeElem = nodes.item(0);
		}
		return nodeElem;
	}

	public void extractProcess2Excel(String inWBMXMLFileName, String outFileName) throws Exception {
		Document wbmDoc = getXMLDocument(inWBMXMLFileName);
		FileOutputStream fstream = new FileOutputStream(outFileName);

	    book = new HSSFWorkbook();
	    sheet = book.createSheet("Report");
	    printHeader(sheet, uc_header, nCurrentRow++);
	    
		extractWBMProcesses(wbmDoc);
		System.out.println("=================================================================");
		try {
			if(out != null) {
				out.close();
			}
			if(book != null) {
				book.write(fstream);
				fstream.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
		
	public void extractProcRules2Excel(String inWBMXMLFileName, String outFileName) throws Exception {

	    book = new HSSFWorkbook();
	    sheet = book.createSheet("RuleSets");
	    printHeader(sheet, ruleset_header, nCurrentRow++);
	    
		Document wbmDoc = null;
		FileOutputStream fstream = null;

		fstream = new FileOutputStream(outFileName);

		String[] inFileNames = inWBMXMLFileName.split("[;]");
		String defWIPT = "";
		for(String infile : inFileNames) {
			int idx = infile.indexOf('_');
			if(idx > 0)
				defWIPT = infile.substring(0, idx);
			wbmDoc = getXMLDocument(infile);
			extractWBMProcRules(wbmDoc, defWIPT);
			System.out.println(infile + "=================================================================");
		}
		try {
			if(out != null) {
				out.close();
			}
			if(book != null) {
				book.write(fstream);
				fstream.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * @param args
	 */
	public static void main(String[] args) {
//		String inWBMXML_RulesFileName = "IAM_Rules.xml;BCM_Rules.xml;ECM_Rules.xml;RFM_Rules.xml;AKM_Rules.xml";	//"wmb_services.xml";
//		String outXMI_RulesFileName = "Process_RuleSets.xls";

		String inWBMXMLFileName = "RFM.xml";
		String outXMIFileName = "RFM.xls";
		
		ReadWBMXMLFile procReader = new ReadWBMXMLFile();
		try {
			//For extracting WBM Processes and tasks to Excel.
			procReader.extractProcess2Excel(inWBMXMLFileName, outXMIFileName);
			//For extracting WBM Rules to Excel.
			//procReader.extractProcRules2Excel(inWBMXML_RulesFileName, outXMI_RulesFileName);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}

}
